﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.NetworkInformation;
using System.ComponentModel;
using System.Net;
using System.Threading;
using System.Windows.Forms;

namespace WinMTR.NET
{

    class RouteWorker : BackgroundWorker
    {
        public IPAddress Address { get; set; }
        public RouteWorker(IPAddress address)
        {
            Address = address;
            WorkerSupportsCancellation = true;
            WorkerReportsProgress = true;
            this.ProgressChanged += new ProgressChangedEventHandler(RouteWorker_ProgressChanged);
            this.DoWork += new DoWorkEventHandler(RouteWorker_DoWork);
        }





        internal SortedDictionary<int, PingWorker> route = null;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void RouteWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            route = new SortedDictionary<int, PingWorker>();
            WinMTR.NET.TraceComponent.TraceArgs traceArgs = (WinMTR.NET.TraceComponent.TraceArgs)e.Argument;
            int maxhops = 20;

            RouteWorker _bw = (RouteWorker)sender;

            Console.WriteLine("WORK {0}", traceArgs.Address);
            PingOptions po = new PingOptions(1, !true);
            Ping ping = new Ping();

            byte[] byteData = MakeByteArray(64);



            int nrhops = 0;
            while (true && !_bw.CancellationPending)
            {
                PingReply reply = ping.Send(traceArgs.Address, traceArgs.PingTimeout, byteData, po);
                //Console.WriteLine("[{0}][{1}][{2}][{3}]", po.Ttl, reply.Address, reply.RoundtripTime, reply.Status);

                PingWorker pWorker = null;
                lock (route)
                {
                    if (!route.ContainsKey(po.Ttl))
                    {
                        pWorker = new PingWorker(reply.Address, po.Ttl, traceArgs.PingTimeout, traceArgs.PingInterval, traceArgs.ResolveNames);
                        route.Add(po.Ttl, pWorker);
                    }
                    else
                    {
                        pWorker = route[po.Ttl];
                    }
                    pWorker.Address = reply.Address;
                    pWorker.RouteWorker = this;


                    if (!pWorker.IsBusy)
                    {
                        pWorker.RunWorkerAsync();
                    }



                    nrhops++;
                    po.Ttl++;
                    _bw.Refresh();
                    Application.DoEvents();
                    Thread.Sleep(1);

                    if (nrhops == maxhops || reply.Address != null && reply.Address.ToString() == traceArgs.Address.ToString())
                    {
                        nrhops = 0;
                        po = new PingOptions(1, !true);
                        while (!_bw.CancellationPending)
                        {
                            Thread.Sleep(100);
                            _bw.Refresh();
                            Application.DoEvents();
                            Thread.Sleep(1);

                        }

                    }
                }

            }

            foreach (var item in route)
            {
                item.Value.CancelAsync();
            }


        }

        void RouteWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {

        }


        public CollectedData Data { get; private set; }
        public bool ShoudRefresh{ get; set; }

        public void Refresh()
        {
            if (Data == null)
                Data = new CollectedData();




          // lock (route)
            {
                

                foreach (var item in route)
                {


                    CollectedDataItem cdi = Data.Items.Find(x => x.Address == null || item.Value.Address == null || x.Address.ToString() == item.Value.Address.ToString());
                    if (cdi == null)
                    {
                        cdi = new CollectedDataItem();
                        Data.Items.Add(cdi);
                    }


                    cdi.Address = item.Value.Address;

                    cdi.Loss = item.Value.Loss;
                    cdi.RecvPings = item.Value.RecvPings;
                    cdi.SentPings = item.Value.SentPings;
                    cdi.Hostname = item.Value.Hostname;
                    cdi.AvgRoundTrip = item.Value.AvgRoundTrip;
                    cdi.BestRoundTrip = item.Value.BestRoundTrip;
                    cdi.LastRoundTrip = item.Value.LastRoundTrip;
                    cdi.TTL = item.Value.TTL;
                    //Console.WriteLine(">>>>>>>>>>>>>>>>>>>>>>>>> {0}", cdi.TTL);

                }

                this.ReportProgress(0);
            }

        }






        /// <summary>
        /// 
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>

        public static byte[] MakeByteArray(int length)
        {
            byte[] byteData = new byte[length];
            for (int i = 0; i < byteData.Length; i++)
            {
                byteData[i] = 0x65;
            }
            return byteData;
        }


    }


}
